﻿using System;
using System.Collections.Generic;
using System.Linq;
using FlipRunner.Physics;
using Microsoft.Xna.Framework;
using XnaPlus.Common;

namespace FlipRunner.Components
{
    public class MapGenerator : GamePlusComponent
    {

        private static readonly ISize MinObstacleSize = new CartesianVector(3f, 0.5f);
        private static readonly ISize MaxObstacleSize = new CartesianVector(7f, 8f);

        private static readonly CartesianVector MaxPlatformPositionDelta = new CartesianVector(2, 2.5f);
        private static readonly CartesianVector MinPlatformPositionDelta = new CartesianVector(-0.1f, 0.2f);

        private CartesianVector m_nextDelta;

        private static readonly Random Random = new Random();

        private new FlipRunnerGame Game { get { return base.Game as FlipRunnerGame; } }

        public List<Platform> Platforms { get; private set; }

        public MapGenerator(GamePlus game)
            : base(game)
        {
            Platforms = new List<Platform>();

        }



        public override void Update(GameTime gameTime)
        {
            base.Update(gameTime);
            UpdatePlatforms(gameTime);
        }

        private Platform TryGetNextPlatform(Platform previous)
        {
            if (previous == null)
            {
                var size = Random.NextCartesianVector((CartesianVector)MinObstacleSize, (CartesianVector)MaxObstacleSize);
                CartesianPoint position = new CartesianPoint(Game.WorldSize.Width, size.Y);
                return new Platform(Game) { Position = position, Size = size };
            }

            if (Game.WorldSize.Width - previous.Right() >= m_nextDelta.X)
            {
                var size = new CartesianVector(Random.NextFloat(MinObstacleSize.Width, MaxObstacleSize.Width),
                                               MathHelper.Clamp(previous.Size.Height + m_nextDelta.Y, MinObstacleSize.Height, MaxObstacleSize.Height));
                var position = new CartesianPoint(previous.Position.X + previous.Size.Width + m_nextDelta.X, size.Y);
                return new Platform(Game) { Position = position, Size = size };
            }

            return null;
        }

        private void UpdatePlatforms(GameTime gameTime)
        {

            Platform previous = Platforms.Count > 0 ? Platforms.Last() : null;

            Platform next = TryGetNextPlatform(previous);

            if (next != null)
            {
                Platforms.Add(next);
                m_nextDelta = Random.NextCartesianVector(MinPlatformPositionDelta, MaxPlatformPositionDelta);
                m_nextDelta = new CartesianVector(m_nextDelta.X, Random.NextSign() * m_nextDelta.Y);
            }

            foreach (var platform in Platforms.Where(p => p.IsCompletelyOutsideWorldBounds()).ToList())
            {
                platform.Dispose();
                Platforms.Remove(platform);
            }
        }

        public float GetMaxPlatformHeight(float minX, float maxX)
        {
            float[] intersectingTops = Platforms.Where(p => p.Right() >= minX && p.Left() <= maxX).Select(p => p.Top()).ToArray();

            if (intersectingTops.Length == 0)
            {
                return 0;
            }

            return intersectingTops.Max();
        }

        /// <summary>
        /// Gets the height of the floor in Meters at the specified xCoordinate
        /// </summary>
        public float GetPlatformHeight(float xCoordinate)
        {
            return (from platform in Platforms
                    select platform.GetCartesianHitBox() into hitBox
                    where hitBox.Left <= xCoordinate && hitBox.Right >= xCoordinate
                    select hitBox.Top).FirstOrDefault();
        }
    }
}